// 常量状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function resolvePromise(promise, x, resolve, reject) {
  // 如果相等了，说明return的是自己，抛出类型错误并返回
  if (promise === x) {
    return reject(
      new TypeError("The promise and the return value are the same")
    );
  }

  if (typeof x === "object" || typeof x === "function") {
    // x 为 null 直接返回，走后面的逻辑会报错
    if (x === null) {
      return resolve(x);
    }

    let then;
    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 error ，则以 error 为据因拒绝 promise
      return reject(error);
    }

    // 如果 then 是函数
    if (typeof then === "function") {
      let called = false;
      try {
        then.call(
          x, // this 指向 x
          // 如果 resolvePromise 以值 y 为参数被调用，则运行 [[Resolve]](promise, y)
          // y 是 then 函数中返回 promise 的 value
          (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被调用，
            // 或者被同一参数调用了多次，则优先采用首次调用并忽略剩下的调用
            // 实现这条需要前面加一个变量 called
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          // 如果 rejectPromise 以据因 r 为参数被调用，则以据因 r 拒绝 promise
          // r 为 then 函数中返回 promise 的 reason
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (error) {
        // 如果调用 then 方法抛出了异常 error：
        // 如果 resolvePromise 或 rejectPromise 已经被调用，直接返回
        if (called) return;

        // 否则以 error 为据因拒绝 promise
        reject(error);
      }
    } else {
      // 如果 then 不是函数，以 x 为参数执行 promise
      resolve(x);
    }
  } else {
    // 如果 x 不为对象或者函数，以 x 为参数执行 promise
    resolve(x);
  }
}

class MyPromise {
  status = PENDING; // 状态
  value = ""; // 缓存 resolve 传递的参数
  reason = ""; // 缓存 reject 传递的参数
  // 缓存then、catch里面调用的函数，为了实现resolve、reject的时候真正执行
  // 以数组的形式缓存，因为可以多次执行then和catch
  cacheObj = {
    onFulfilledCbs: [],
    onRejectedCbs: [],
  };
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }
  // 为了避免传递后this指向丢失，resolve 和 reject 使用箭头函数
  resolve = (value) => {
    // 修改状态
    if (this.status !== PENDING) {
      return;
    }
    this.status = FULFILLED;
    this.value = value;

    // resolve里面将所有成功的回调拿出来执行
    while (this.cacheObj.onFulfilledCbs.length) {
      // Array.shift() 取出数组第一个元素，然后（）调用，shift不是纯函数，取出后，数组将失去该元素，直到数组为空
      this.cacheObj.onFulfilledCbs.shift()(value);
    }
  };
  reject = (reason) => {
    // 修改状态
    if (this.status !== PENDING) {
      return;
    }
    this.status = REJECTED;
    this.reason = reason;

    while (this.cacheObj.onRejectedCbs.length) {
      this.cacheObj.onRejectedCbs.shift()(reason);
    }
  };
  then(onFulfilled, onRejected) {
    // 如果不传，就使用默认函数
    const realOnFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    const realOnRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };

    // 返回一个promise，目的是为了实现链式调用
    const nextP = new MyPromise((resolve, reject) => {
      let result = "";

      const fulfilledMicrotask = () => {
        // 创建一个微任务等待 nextP 完成初始化
        // console.log("加入微任务", this.value);
        queueMicrotask(() => {
          try {
            result = realOnFulfilled(this.value);
            resolvePromise(nextP, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      const rejectedMicrotask = () => {
        // 创建一个微任务等待 promise2 完成初始化
        // console.log("加入微任务", this.reason);
        queueMicrotask(() => {
          try {
            // 调用失败回调，并且把原因返回
            const result = realOnRejected(this.reason);
            // 传入 resolvePromise 集中处理
            resolvePromise(nextP, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      // 通过状态执行对应的回调
      if (this.status === FULFILLED) {
        fulfilledMicrotask();
      } else if (this.status === REJECTED) {
        // 状态已经发生改变
        // 调用失败回调，并且把原因返回
        rejectedMicrotask();
      } else if (this.status === PENDING) {
        // 这儿必须马上加入队列，这样在上面两种请尽快执行微任务的时候，
        // 才能在队列里面找到对应的回调函数（测试在then里面返回自己时，可以在catch中获取）
        this.cacheObj.onFulfilledCbs.push(fulfilledMicrotask);
        this.cacheObj.onRejectedCbs.push(rejectedMicrotask);
      }
    });
    return nextP;
  }
  // catch 处理
  catch(onRejected) {
    const nextP = new MyPromise((resolve, reject) => {
      if (this.status === REJECTED) {
        queueMicrotask(() => {
          // 调用失败回调，并且把原因返回
          const result = onRejected(this.reason);
          resolvePromise(nextP, result, resolve, reject);
        });
      } else if (this.status === PENDING) {
        // 此时说明是异步reject，所以缓存回调
        if (onRejected) {
          // this.cacheObj.onRejected.push(onRejected);
          this.cacheObj.onRejectedCbs.push({
            cb: onRejected,
            resolve,
            reject,
            // nextP,
          });
        }
      }
    });
    return nextP;
  }
  static resolve = (val) => {
    // 如果传入 MyPromise 就直接返回
    if (val instanceof MyPromise) {
      return val;
    }
    return new MyPromise((resolve, reject) => {
      resolve(val);
    });
  };
  static reject = (val) => {
    return new MyPromise((resolve, reject) => {
      reject(val);
    });
  };
}

// 新建 MyPromise 类

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
};
module.exports = MyPromise;

// 打印内容：0、1、2、4、3、5、6
// v8 的打印内容为：0、1、2、3、4、5、6（因为在内部 return MyPromise.resolve(4) 会有两次加入微任务的操作）
MyPromise.resolve()
  .then(() => {
    console.log(0); // 已打印
    return MyPromise.resolve(4); // 第一次加入微任务是执行then的时候，微任务的内容是执行resolve，然后把A对应的内容加入微任务
  })
  .then((res) => {
    console.log(res);
  });

MyPromise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  });
